﻿using Amib.Threading;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
//using System.Threading;
using System.Timers;

namespace IOPerformanceVerifier
{
    public class VerifierMain
    {
        private static PerformanceLogger _performanceLogger;

        static void Main(string[] args)
        {
            if (args.Length != 3)
            {
                Console.Write(writeHelp());
                return;
            }
            VerifierMain verifierMain;
            try
            {
                verifierMain = new VerifierMain(args);
                verifierMain.Initiate();
                Console.WriteLine("Verification finished");
                verifierMain.StartTasks();
                while (!verifierMain.IsTasksShutDown)
                {
                    // Sleep 5 sec
                    System.Threading.Thread.Sleep(5000);

                    // Log data
                    _performanceLogger.logPerformanceCounters();
                }
                _performanceLogger.stopLogging();
            }
            catch (Exception e)
            {
                Console.WriteLine("Error occured: " + e.Message);
            }
            Console.WriteLine("Press Enter to finish");
            Console.ReadLine();
        }

        private IOPVFactory _iopvFactory = null;
        private Logger _logger = null;
        private float _pressureMultiplier;
        private String _configurationFile;
        private int _testTime;
        private bool _isTasksShutDown = false;
        private Dictionary<string, ThreadConfigutation> _threadConfigurationDictionary = null;
        private List<ThrottlerThread> _throttlerThreadList = null;
        private SmartThreadPool _smartThreadPool;
        private System.Timers.Timer _timer;

        public bool IsTasksShutDown
        {
            get
            {
                return _isTasksShutDown;
            }
        }

        private void StartTasks()
        {
            // Start threads
            StartThreads();
            // Start Logging
            _performanceLogger.startLogging();

            StartTimer(_testTime);
        }

        private void StartThreads()
        {

            foreach (ThrottlerThread throttlerThread in _throttlerThreadList)
            {
                ThreadConfigutation threadConfiguration;
                if (_threadConfigurationDictionary.TryGetValue(throttlerThread.InternalDiskTestName, out threadConfiguration))
                {
                    _smartThreadPool.QueueWorkItem(throttlerThread.ThreadPoolCallback, threadConfiguration);
                }
                else
                {
                    _logger.log(LogLevel.Error, throttlerThread.InternalDiskTestName + "Thread not found");
                }
            }
            _logger.log(LogLevel.Debug, "Throttler thread objects queued.");
            _smartThreadPool.Start();
            _logger.log(LogLevel.Debug, "Thread pool started.");
        }

        private void Initiate()
        {
            // Load job configuration
            List<ThreadConfigutation> threadConfigurationList = _iopvFactory.loadConfigurations(_pressureMultiplier,_configurationFile);
            _threadConfigurationDictionary = new Dictionary<string, ThreadConfigutation>();
            _throttlerThreadList = new List<ThrottlerThread>();
            _logger.log(LogLevel.Debug, "Thread configuration loaded.");

            foreach (ThreadConfigutation threadConfiguration in threadConfigurationList)
            {
                _threadConfigurationDictionary.Add(generateDiskTestName(threadConfiguration), threadConfiguration);
                ThrottlerThread throttlerThread = new ThrottlerThread(threadConfiguration.DiskTestName, threadConfiguration.InternalDiskTestName);
                _throttlerThreadList.Add(throttlerThread);
                if (throttlerThread.inititateWork(threadConfiguration))
                {
                    _logger.log(LogLevel.Debug, "Throttler thread initialized.");
                }
                else
                {
                    _logger.log(LogLevel.Error, "Throttler thread not initialized. Ignored.");
                }
            }
            _logger.log(LogLevel.Debug, "Thread configuration dictionary created.");
            _performanceLogger = new PerformanceLogger(_threadConfigurationDictionary);
            if (_performanceLogger != null)
            {
                _logger.log(LogLevel.Debug, "Performance Logger exists");
            }
            _performanceLogger.initialize();
            _logger.log(LogLevel.Debug, "PerformanceLogger created.");
        }

        private string generateDiskTestName(ThreadConfigutation threadConfiguration)
        {
            return threadConfiguration.DiskTestName + "_" + threadConfiguration.ThreadType.ToString() + "_" + threadConfiguration.ThreadNumber;
        }

        public VerifierMain(string[] args)
        {
            _iopvFactory = IOPVFactory.getInstance();
            _logger = _iopvFactory.getLogger();
            STPStartInfo stpStartInfo = new STPStartInfo();
            stpStartInfo.AreThreadsBackground = true;
            _smartThreadPool = new SmartThreadPool(stpStartInfo);

            //Input pressure multiplier
            _pressureMultiplier = verifyPressureMultiplier(args[0]);
            _logger.log(LogLevel.Debug, "Pressure multiplier: " + _pressureMultiplier);

            //Input configuration
            _configurationFile = verifyConfigurationFile(args[1]);
            _logger.log(LogLevel.Debug, "Configuration: " + _configurationFile);

            //Input testtime in minutes
            _testTime = verifyTestTime(args[2]);
            _logger.log(LogLevel.Debug, "Test time: " + _testTime);
        }

        private int verifyTestTime(string testTime)
        {
            int innerTestTime;
            if (!int.TryParse(testTime, out innerTestTime))
            {
                throw new ArgumentException("Test time contains error", "testTime");
            }
            if (innerTestTime < 1)
            {
                throw new ArgumentException("Test time is too low or negative", "testTime");
            }
            return innerTestTime;
        }

        private string verifyConfigurationFile(string configurationFile)
        {
            if(!File.Exists(configurationFile))
            {
                throw new ArgumentException("Configuration file does not exist", "configurationFile");
            }

            // Get absolute path if not one already
            String absolueFileName = Path.GetFullPath(configurationFile);
            return absolueFileName;
        }

        private float verifyPressureMultiplier(string pressureMultiplier)
        {
            float innerFloat;
            string trimmedPressureMultiplier = pressureMultiplier.Replace(",", ".").Trim();
            if (!float.TryParse(trimmedPressureMultiplier, NumberStyles.Any, CultureInfo.InvariantCulture, out innerFloat))
            {
                throw new ArgumentException("Pressure multiplier contains error", "pressureMultiplier");
            }
            if (innerFloat < 0.1)
            {
                throw new ArgumentException("Pressure multiplier is too low or negative", "pressureMultiplier");
            }
            return innerFloat;
        }

        private static string writeHelp()
        {
            StringBuilder stringBuilder = new StringBuilder();

            stringBuilder.AppendLine("Use IOPV [pressure multiplier] [configuration file] [test time in minutes]");

            return stringBuilder.ToString();
        }

        public void shutdownThreads(object sender, EventArgs e)
        {
            _timer.Dispose();
            _logger.log(LogLevel.Debug, "Times up");

            // Send Shut down to all threads
            _smartThreadPool.Cancel(true);
            _logger.log(LogLevel.Debug, "Cancel sent");
            // Wait for all threads to shut down
            _smartThreadPool.WaitForIdle();

            _logger.log(LogLevel.Debug, "Wait for shutdown finished");

            _smartThreadPool.Shutdown();
            _isTasksShutDown = true;
        }

        private void StartTimer(int testTime)
        {

            _timer = new System.Timers.Timer();

            _timer.Elapsed += new ElapsedEventHandler(shutdownThreads);
            _timer.Interval = (1000) * (60) * testTime;
            _timer.Enabled = true;
            _timer.Start();
            _logger.log(LogLevel.Debug, "Timer started for " + testTime + " minutes");
        }
    }
}
